#include "easylogging++.h"
#include "kcpserver.h"

INITIALIZE_EASYLOGGINGPP


extern "C" {
#include <stdio.h>
#include "erl_nif.h"


/*
static ERL_NIF_TERM
test_run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 
{
	ERL_NIF_TERM r;
	r = enif_make_int(env, 4);
	return enif_schedule_dirty_nif_finalizer(env, r, enif_dirty_nif_finalizer);
}
*/


static ERL_NIF_TERM
test_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 
{
	printf("good night %d", argc);
	int ret = 1024;
	el::Configurations defaultConf;
    defaultConf.setToDefault();
    defaultConf.set(el::Level::Info, el::ConfigurationType::Enabled, "true");
    defaultConf.set(el::Level::Info, el::ConfigurationType::Format, "%datetime %msg");
    defaultConf.set(el::Level::Info, el::ConfigurationType::ToFile, "true");
    defaultConf.set(el::Level::Info, el::ConfigurationType::Filename, "test.log");
    el::Loggers::reconfigureLogger("default", defaultConf);

    LOG(INFO) << "Start";
	return enif_make_int(env, ret);
}

static ERL_NIF_TERM
receiveUdp_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 
{
	char ip[128];
	char port[32];
	ErlNifBinary data;

	int ret = enif_get_string(env, argv[0], ip, 128, ERL_NIF_LATIN1);
	if(!ret) {
		return enif_make_badarg(env);
	}
	LOG(INFO) << ip;
	ret = enif_get_string(env, argv[1], port, 32, ERL_NIF_LATIN1);
	if(!ret) {
		return enif_make_badarg(env);
	}
	LOG(INFO) << port;
	
	ret = enif_inspect_binary(env, argv[2], &data);
	if(!ret) {
		return enif_make_badarg(env);
	}

	LOG(INFO) <<data.size;
	
	auto Data = shared_ptr< std::vector<unsigned char> >(new std::vector<unsigned char>());
	Data->resize(data.size);
	memcpy(&((*Data)[0]), data.data, data.size);
	KCPServer::GetInstance().OnReceiveUDP(ip, port, Data);
	
	return enif_make_int(env, 0);
}


static ERL_NIF_TERM
updateCpp_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 
{
	KCPServer::GetInstance().Update();
	return enif_make_int(env, 0);
}

static ERL_NIF_TERM
getUDPPackets_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 
{
	LOG(INFO) << "getUDPPackets";

	auto packs = KCPServer::GetInstance().GetSendUDPPackets();
	//ipport data, ipport data, ipport data
	auto count = packs->size();
	std::vector<ERL_NIF_TERM> arr;
	//arr.resize(count*2);
	arr.reserve(count*2);
	
	LOG(INFO) << "Count2:" << count;
	for(auto i = 0; i < count; i++) {
		auto p = (*packs)[i];
		auto re = p->remoteEnd;
		auto data = p->GetFullData();
		auto str = enif_make_string(env, re.c_str(), ERL_NIF_LATIN1);
		LOG(INFO) << "PacketStr2";

		ErlNifBinary bin;
		if (!enif_alloc_binary(data->size(), &bin)) {
			return enif_make_badarg(env);
		}
		memcpy(bin.data, &(*data)[0], data->size());

		auto bindata = enif_make_binary(env, &bin);

		LOG(INFO) << "Binary2";

		arr.push_back(str);
		arr.push_back(bindata);
	}
	packs->clear();

	LOG(INFO) << "LIST2:" << arr.size();
	if(arr.size() == 0){
		return enif_make_list_from_array(env, NULL, arr.size());
	}else {
		return enif_make_list_from_array(env, &arr[0], arr.size());
	}
}


static ERL_NIF_TERM
getKCPPackets_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 
{
	LOG(INFO) << "getKCPPackets";

	auto packs = KCPServer::GetInstance().GetReceiveKcpPackets();
	auto count = packs->size();
	std::vector<ERL_NIF_TERM> arr;
	//arr.resize(count*2);
	arr.reserve(count*2);

	for(auto i = 0; i < count; i++) {
		auto p = (*packs)[i];
		auto re = p->remoteEnd;
		auto data = p->data;

		auto str = enif_make_string(env, re.c_str(), ERL_NIF_LATIN1);
		LOG(INFO) << "PacketStr";

		ErlNifBinary bin;
		if (!enif_alloc_binary(data->size(), &bin)) {
			return enif_make_badarg(env);
		}
		memcpy(bin.data, &(*data)[0], data->size());

		auto bindata = enif_make_binary(env, &bin);
		LOG(INFO) << "Binary";

		arr.push_back(str);
		arr.push_back(bindata);
	}
	packs->clear();
	LOG(INFO) << "LIST:"<<arr.size();
	if(arr.size() == 0) {
		return enif_make_list_from_array(env, NULL, arr.size());
	}else {
		return enif_make_list_from_array(env, &arr[0], arr.size());
	}
}

static ERL_NIF_TERM 
sendData_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
	char ip[128];
	char port[32];
	ErlNifBinary data;

	int ret = enif_get_string(env, argv[0], ip, 128, ERL_NIF_LATIN1);
	if(!ret) {
		return enif_make_badarg(env);
	}
	LOG(INFO) << ip;
	ret = enif_get_string(env, argv[1], port, 32, ERL_NIF_LATIN1);
	if(!ret) {
		return enif_make_badarg(env);
	}
	LOG(INFO) << port;
	
	ret = enif_inspect_binary(env, argv[2], &data);
	if(!ret) {
		return enif_make_badarg(env);
	}

	LOG(INFO) <<data.size;
	
	auto Data = shared_ptr< std::vector<unsigned char> >(new std::vector<unsigned char>());
	Data->resize(data.size);
	memcpy(&((*Data)[0]), data.data, data.size);
	KCPServer::GetInstance().SendData(ip, port, Data);
	return enif_make_int(env, 0);
}

static ERL_NIF_TERM 
closeAgent_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
	char udpPort[128];

	int ret = enif_get_string(env, argv[0], udpPort, 128, ERL_NIF_LATIN1);
	if(!ret) {
		return enif_make_badarg(env);
	}
	LOG(INFO) << udpPort;

	KCPServer::GetInstance().CloseAgent(udpPort);
	return enif_make_int(env, 0);
}


static ERL_NIF_TERM
getClosedAgent_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 
{
	LOG(INFO) << "getClosedAgent";

	auto agents = KCPServer::GetInstance().GetClosedAgents();
	auto count = agents->size();
	std::vector<ERL_NIF_TERM> arr;
	arr.reserve(count);
	for(auto i = 0; i < count; i++) {
		auto p = (*agents)[i];
		auto re = p->remoteEnd;
		
		auto str = enif_make_string(env, re.c_str(), ERL_NIF_LATIN1);
		LOG(INFO) << "PacketStr";

		arr.push_back(str);
	}
	agents->clear();
	LOG(INFO) << "LIST:"<<arr.size();
	if(arr.size() == 0) {
		return enif_make_list_from_array(env, NULL, arr.size());
	}else {
		return enif_make_list_from_array(env, &arr[0], arr.size());
	}
}

static ErlNifFunc nif_funcs[] = {
	{"test", 0, test_nif},
	{"receiveUdp", 3, receiveUdp_nif},
	{"updateCpp", 0, updateCpp_nif},
	{"getUDPPackets", 0, getUDPPackets_nif},
	{"getKCPPackets", 0, getKCPPackets_nif},
	{"sendData", 3, sendData_nif},
	{"closeAgent", 1, closeAgent_nif},
	{"getClosedAgent", 0, getClosedAgent_nif},
};

ERL_NIF_INIT(kcpserver, nif_funcs, NULL, NULL, NULL, NULL)

}